from datetime import datetime, timedelta, timezone
from typing import Union

import jwt
from fastapi import Header, Depends
from fastapi.security import OAuth2PasswordBearer
from jwt import ExpiredSignatureError, InvalidTokenError
from passlib.context import CryptContext
from sqlalchemy.orm import Session

from crud import UserAdminCrud
from util.DataBaseTools import get_db_session
from util.ErrorCode import ErrorCode

SECRET_KEY = "52f5e4e6d20bf03f4fa4a9617bbcf167f85a6db8cfac240e36708f474d719d73"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 1500

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password):
    return pwd_context.hash(password)


def authenticate_user(password: str, hashed_password):
    if not verify_password(password, hashed_password):
        return False
    else:
        return True


def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def check_token(db: Session = Depends(get_db_session), token: str = Header(...)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        user_id: str = payload.get("sub")
        if user_id is not None:
            response = UserAdminCrud.query_user_by_userid(user_id, db)
            if response.get('code') == 0:
                data = response.get('data')
                if data is not None:
                    return response
                else:
                    return {'code': ErrorCode.INVALID_TOKEN.value, 'message': 'invalid token!'}
            else:
                return {'code': ErrorCode.INVALID_TOKEN.value, 'message': 'invalid token!'}
    except ExpiredSignatureError:
        return {'code': ErrorCode.INVALID_TOKEN.value, 'message': 'invalid token!'}
    except InvalidTokenError:
        return {'code': ErrorCode.INVALID_TOKEN.value, 'message': 'invalid token!'}
    except Exception as e:
        return {'code': ErrorCode.INVALID_TOKEN.value, 'message': 'invalid token!'}

